package com.platform.boot.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * @author Alex bob(<a href="https://github.com/vnobo">https://github.com/vnobo</a>)
 */
public class ReactiveSecurityHelper {

    public static final String SECURITY_TOKEN_CONTEXT = "SECURITY_USERS_CONTEXT";
    public static final String SECURITY_SYSTEM_HEADER = "X-System";
    public static final String SECURITY_AUTH_TOKEN_HEADER = "X-Auth-Token";

    /**
     * 认证登录,设置登录后的session
     *
     * @param exchange 请求session
     * @return 登录后的 token
     */
    public static Mono<AuthenticationToken> authenticationTokenMono(
            ServerWebExchange exchange, Authentication authentication) {
        return exchange.getSession().doOnNext(session -> session.getAttributes().put(
                        WebSessionServerSecurityContextRepository
                                .DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, new SecurityContextImpl(authentication)))
                .delayUntil(WebSession::changeSessionId)
                .map(AuthenticationToken::build)
                .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
    }

    /**
     * 将Token保存在Session
     *
     * @param exchange        绘画请求
     * @param securityDetails 用户认证信息
     * @return 无返回
     */
    public static Mono<Void> saveToken(ServerWebExchange exchange, SecurityUserDetails securityDetails) {
        return exchange.getSession()
                .doOnNext((session) -> putToken(session.getAttributes(), securityDetails))
                .flatMap(WebSession::save);
    }

    /**
     * 将token 保存session map中
     *
     * @param attributes      session map {@link WebSession#getAttributes()}
     * @param securityDetails 用户认证信息
     */
    private static void putToken(Map<String, Object> attributes, SecurityUserDetails securityDetails) {
        if (ObjectUtils.isEmpty(securityDetails.getUserCode())) {
            attributes.remove(SECURITY_TOKEN_CONTEXT);
        } else {
            attributes.put(SECURITY_TOKEN_CONTEXT, securityDetails);
        }
    }

    /**
     * 清除会话上下文的用户认证信息
     *
     * @param exchange 会话上下文
     * @return 无返回
     */
    public static Mono<Void> removeToken(ServerWebExchange exchange) {
        return exchange.getSession()
                .doOnNext(session -> session.getAttributes().remove(SECURITY_TOKEN_CONTEXT))
                .flatMap(WebSession::save)
                .contextWrite(context -> ReactiveSecurityUserHolder.clearContext().apply(context));
    }
}